var Spectrum = function(container, width, height, peaks) {

  Raphael.fn.line = function(x1, y1, x2, y2) {
    return this.path('M' + x1 + ' ' + y1 + ' L' + x2 + ' ' + y2)
  };

  Raphael.el.sharpen = function() {
    return this.translate(0.5, 0.5);
  };

  Raphael.el.useDefaultTextAttributes = function(options) {
    return this.attr($.extend({
      'font-family': 'Helvetica, Arial, sans-serif;',
      'font-size': width / 80.0 // Selected by rigorous Stetson-harrison testing.
    }, options));
  };

  var paper = Raphael(container, width, height);
  var padding = width * 0.045;
  var maxIntensity = Math.max.apply(Math, $.map(peaks, function(peak) { return peak.intensity; })) * 1.1;
  var maxMass = Math.max.apply(Math, $.map(peaks, function(peak) { return peak.mass; })) * 1.1;

  // Transforms the given mass (x-coordinate) to the coorsponding x-coordinate
  // on the canvas.
  var transformMass = function(mass) {
    return mass / maxMass * (width - padding) + padding;
  };

  // Transforms the given intensity (y-coordinate) to the coorsponding
  // y-coordinate on the canvas.
  var transformIntensity = function(intensity) {
    return height - padding - (intensity / maxIntensity) * (height - padding);
  };

  var drawImageBorder = function() {
    paper.rect(0.5, 0.5, width - 1, height - 1);
  }

  var drawSpectrumAxes = function() {
    paper.rect(padding, 0, width - padding, height - padding).sharpen();
  }

  var drawYAxisMarkers = function(maxIntensity, step) {
    for (intensity = step; intensity < maxIntensity; intensity += step) {
      var paperIntensity = transformIntensity(intensity);
      paper.line(padding - 3, paperIntensity, padding + 3, paperIntensity).sharpen();
      paper.text(padding / 1.75, paperIntensity, intensity).useDefaultTextAttributes();
    }

    paper.text(6, height / 2.0 - 15, 'Intensity')
      .useDefaultTextAttributes({ fill: 'gray'})
      .rotate(270);
  }

  var drawXAxisMarkers = function(maxMass, step) {
    for (mass = step; mass < maxMass; mass += step) {
      var paperMass = transformMass(mass);
      paper.line(paperMass, height - padding - 3, paperMass, height - padding + 3).sharpen();
      paper.text(paperMass, height - padding / 1.5, mass).useDefaultTextAttributes();
    }

    paper.text(width / 2.0, height - 10, 'Mass (m/z)')
      .useDefaultTextAttributes({ fill: 'gray'});
  }

  var drawPeaks = function() {
    $.each(peaks, function(peakNum, peak) { drawPeak(peak); });
  }

  var drawPeak = function(peak) {
    var paperMass = transformMass(peak.mass);
    var paperIntensity = transformIntensity(peak.intensity);

    var bar = paper.line(paperMass, transformIntensity(0), paperMass, paperIntensity).sharpen();

    bar.attr({
      stroke: peak.state == 'Selected' ? 'green' : peak.state == 'Found' ? '#FF6501' : '#BF211E',
      'stroke-width': 3,
    });

    bar.hover(function() {
      bar.animate({ 'stroke-width': 4 }, 200).toFront();
    }, function() {
      bar.animate({ 'stroke-width': 3 }, 200);
    });

    $(bar.node).qtip({
      content: 'Intensity: ' + peak.intensity + "<br />" + 'Mass: ' + peak.mass + ' m/z' + '<br />' + 'State: ' + (peak.state),
      style: { name: 'cream', tip: true },
      position: { corner: 'bottomLeft', target: 'mouse' },
      show: 'mouseover',
      hide: 'mouseout',
    }).mouseover(function() {
      this.style.cursor = 'pointer';
    }).click(function() {
      $('#peaks').tabs('select', '#peak-' + peak.num);
    });
  }

  var setupCanvas = function() {
    drawImageBorder();
    drawSpectrumAxes();
    drawYAxisMarkers(maxIntensity, 100);
    drawXAxisMarkers(maxMass, 25);
  }

  setupCanvas();
  drawPeaks();
  return paper;
};
